﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GameEngine.Players;

namespace GameEngine
{
    public abstract class Engine
    {
        public enum Modes
        {
            Simultaneous,          // all players declare and action at same time, e.g. scissors paper rock
            TurnBased,             // a single player has their complete go (declare multiple and action multiple) and then the next
            SimultaneousTurnBased  // players choose their move at the same time, then declare them all at the same time, then execute them in turn order
        };

        public Modes Mode { get; private set; }
        public Engine(Modes mode)
        {
            Mode = mode;
        }

        public List<Players.Player> Players { get; private set; } = new List<Players.Player>();

        protected class PlayerMove
        {
            public Players.Player Player { get; set; }

            public Moves.Move Move { get; set; }
        }

        public class MoveDetails
        {
            public Moves.Move Move { get; set; }
            public List<string> IllegalMoves { get; set; }
        }

        protected class PlayerMoveCollection : System.Collections.ObjectModel.KeyedCollection<Players.Player, PlayerMove>
        {
            protected override Player GetKeyForItem(PlayerMove item)
            {
                return item.Player;
            }
        }

        protected abstract bool IsMoveLegal(PlayerMove playerMove, List<string> reasons);

        protected abstract void DeclareMoves(IEnumerable<PlayerMove> playerMoves);

        protected abstract bool ActionMoves(IEnumerable<PlayerMove> playerMoves);


        private EventWaitHandle _stopEvent;
        private System.Threading.Thread _thread;
        public void Start()
        {
            System.Diagnostics.Debug.Assert(_thread == null);
            _stopEvent = new EventWaitHandle(false, EventResetMode.ManualReset);
            _thread = new Thread(ThreadMain);
            
            _thread.Start();
        }

        private void MoveHandler(PlayerMoveCollection playerMoveCollection, Players.Player player, MoveDetails details)
        {
            var playerMove = new PlayerMove
            {
                Player = player,
                Move = details.Move
            };

            lock (playerMoveCollection)
            {
                var illegalReasons = new List<string>();
                if (!IsMoveLegal(playerMove, illegalReasons))
                {
                    details.IllegalMoves = illegalReasons;
                }

                playerMoveCollection.Add(playerMove);
            }
        }
        private void ThreadMain()
        {
            bool gameComplete = false;
            while(!gameComplete && !_stopEvent.WaitOne(0))
            {
                if( Mode == Modes.SimultaneousTurnBased)
                {
                    // ask each player for their moves
                    var playerMoves = new PlayerMoveCollection();
                    Players.ForEach(
                        player =>
                            player.GetNextMoveAsync(
                                details =>
                                MoveHandler(playerMoves, player, details))
                        );

                    // wait till they have made them
                    bool allMovesMade = false;
                    while (!allMovesMade)
                    {
                        lock (playerMoves)
                        {
                            allMovesMade = playerMoves.Count == Players.Count;
                        }
                    }

                    DeclareMoves(playerMoves);

                    // action the moves
                    gameComplete = ActionMoves(playerMoves);
                }
                else
                {
                    throw new NotImplementedException();
                }
                
            }
            // cleanup
        }

        public void Stop( bool waitForStop = false )
        {
            _stopEvent.Set();
            while(true)
            {
                bool alive;
                lock (_thread)
                {
                    alive = _thread.IsAlive;
                }
                if (!alive)
                {
                    return;
                }

                System.Threading.Thread.Sleep(20);
            }
        }
    }
}
